<template>
  <!-- 基于vue实现上拉加载和下拉刷新 -->
  <div
    class="scroll-container"
    :class="{
      down: state === 0,
      up: state === 1,
      refresh: state === 2,
      touch: touching,
    }"
    @touchstart="touchstart"
    @touchmove="throttleMove"
    @touchend="touchend"
    @scroll="OnScroll"
  >
    <!-- 内容区域 -->

    <section
      class="inner"
      ref="inner"
      :style="{ transform: `translate3d(0,${top}px,0)` }"
    >
      <header name="pull-refresh" ref="pullHeader">
        <div class="down-tip">
          <img
            src="../assets/refresh_arrow.png"
            :style="{
              transform: `rotate(${
                top * (180 / offset) > 180 ? 180 : top * (180 / offset)
              }deg)`,
              transition: transition,
            }"
            alt=""
          />
        </div>
        <div class="up-tip">
          <img
            src="../assets/refresh_arrow.png"
            :style="{
              transform: `rotate(${180}deg)`,
            }"
            alt=""
          />
        </div>
        <div class="refresh-tip">
          <img class="loading" src="../assets/loading.gif" alt="" />
        </div>
      </header>
      <!-- 中间内容的区域 -->
      <slot></slot>
      <footer class="load-more" ref="footerMore">
        <img
          class="loading"
          v-show="!enableInfinite"
          src="../assets/loading.gif"
          alt=""
        />
        <span class="no-data-text" v-show="enableInfinite"
          >---- 已经到底了 ----</span
        >
      </footer>
    </section>
  </div>
</template>

<script>
export default {
  props: {
    offset: {
      type: Number,
      default: 40,
    },
    onRefresh: {
      type: Function,
      default: () => {},
    },
    //是否可以下拉刷新
    enableRefresh: {
      type: Boolean,
      default: true,
    },
    //是否可以上拉加载
    enableInfinite: {
      type: Boolean,
      default: false,
    },
    //滑动到底部
    onInfinite: {
      type: Function,
      default: () => {},
    },
    //距离底部的距离进行滑动
    distanceOfBottom: {
      type: Number,
      default: undefined,
    },
  },
  data() {
    return {
      //当前组件的状态 //0 正常 1下拉刷新 2 刷新中
      state: 0,
      //获取当前的高度
      top: 0,
      // 触摸滑动开始的位置
      startY: 0,
      transition: "all 0.2s",
      //是否开始滑动
      touching: false,
      //底部加载中的loading
      infiniteLoading: false,
    };
  },

  methods: {
    touchstart(e) {
      this.startY = e.touches[0].pageY;
      //获取当前inner的高度
      console.log("页面的总高度", this.$refs.inner.clientHeight);
      this.touching = true;
      this.startScroll = this.$el.scrollTop || 0;
      console.log("获取当前头部滚动的页面", this.$el.scrollTop);
      console.log("我滑动了", e, this.startY);
    },
    touchmove(e) {
      // 判断是否滑动到顶部的区域
      if (this.$el.scrollTop > 0 || !this.enableRefresh) {
        return;
      }
      //获取手指滑动的距离
      let diff = (e.touches[0].pageY - this.startY) / 3;
      //解决浏览器中下拉默认的滑动事件
      if (diff > 0) e.preventDefault();
      this.top = diff;
      console.log("获取的top的值", this.top);
      //   判断当前是否属于刷新中
      if (this.state === 2) {
        return;
      }
      if (this.top >= this.offset) {
        console.log("走到了这里");
        this.state = 1;
      } else {
        this.state = 0;
      }
      console.log(
        "获取的e",
        e.touches[0].pageY,
        "startScroll",
        this.startScroll,
        " this.$el.scrollTop",
        this.$el.scrollTop
      );
    },
    throttleMove(e) {
      this.throttle(this.touchmove(e), 200);
    },
    touchend(e) {
      if (!this.enableRefresh) {
        return;
      }
      console.log("滑动结束的信息", e);
      this.touching = false;
      if (this.state === 2) {
        // in refreshing
        this.state = 2;
        this.top = this.offset;
        return;
      }
      // 判断当前top的高度
      if (this.top > this.offset) {
        this.refresh();
        setTimeout(() => {
          this.refreshDone();
        }, 2000);
      } else {
        this.refreshDone();
      }
    },
    refreshDone() {
      this.state = 0;
      this.top = 0;
    },
    refresh() {
      this.state = 2;
      this.top = this.offset;
      this.onRefresh(this.refreshDone);
    },
    //滚动到指定位置
    scrollTo() {
      this.$nextTick(() => {
        document.documentElement.scrollTop = 1000;
        window.scrollTo(0, 1000);
      });
      console.dir(this.$refs.inner);
    },
    //上拉加载
    infinite() {
      this.infiniteLoading = true;
      this.onInfinite(this.endInfiniteLoading);
    },
    endInfiniteLoading() {
      this.infiniteLoading = false;
    },
    OnScroll(e) {
      //不能滑动或者是正在加载中时
      if (this.enableInfinite || this.infiniteLoading) {
        return;
      }
      console.log("获取的加载的e", e);
      // 获取当前设备的高度
      let outerHeight = this.$el.clientHeight;
      console.log("outerHeight", outerHeight);
      //获取列表总长度
      let innerHeight = this.$refs.inner.clientHeight;
      console.log("innerHeight", innerHeight);
      //获取已浏览页面的长度
      let scrollTop = this.$el.scrollTop;
      console.log("获取的scrollTop", scrollTop);
      //获取下拉的高度
      let pullHeader = this.$refs.pullHeader.clientHeight;
      console.log("pullHeader", pullHeader);
      // 获取底部的bottom的高度
      let bottomHeight =
        this.distanceOfBottom || this.$refs.footerMore.clientHeight;
      //距离底部的高度
      let bottom = innerHeight - outerHeight - scrollTop - pullHeader;
      console.log("获取的bottom的值", bottom);
      console.log("获取的bottomHeight", bottomHeight);
      if (bottom < bottomHeight) {
        console.log("获取的bottom", bottom);
        console.log("滑动到了底部");
        this.infinite();
      }
    },
    throttleScroll(e) {
      this.throttle(this.OnScroll(e), 200);
    },
    //
    throttle(fn, delay) {
      let now, lastExec, timer, context, args;
      let execute = function (params) {
        fn.apply(context, args);
      };
      return function () {
        context = this;
        args = arguments;
        now = Date.now();
        if (timer) {
          clearTimeout(timer);
          timer = null;
        }
        if (lastExec) {
          let diff = delay - (now - lastExec);
          if (diff < 0) {
            execute();
          } else {
            timer = setTimeout(() => {
              execute();
            }, diff);
          }
        } else {
          execute();
        }
      };
    },
  },
};
</script>

<style lang='scss' scoped >
.scroll-container {
  position: absolute;
  top: 2.5rem;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: auto;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;
  -webkit-overflow-scrolling: touch;
  background-color: #ddd;
  width: 100%;
  -webkit-transform-origin: left top;
  -webkit-transform: translateZ(0);
  -moz-transform-origin: left top;
  -moz-transform: translateZ(0);
  -ms-transform-origin: left top;
  -ms-transform: translateZ(0);
  -o-transform-origin: left top;
  -o-transform: translateZ(0);
  transform-origin: left top;
  transform: translateZ(0);
  .inner {
    position: absolute;
    top: -2rem;
    width: 100%;
    transition-duration: 300ms;
  }
  .pull-refresh {
    position: relative;
    left: 0;
    top: 0;
    width: 100%;
    height: 2rem;
    display: flex;
    align-items: center;
    text-align: center;
    justify-content: center;
  }
}
.scroll-container.touch .inner {
  transition-duration: 0ms;
}
.scroll-container.down .down-tip {
  display: block;
  text-align: center;
}
.rotageImg {
  transform: rotage;
}
.scroll-container.up .up-tip {
  display: block;
  text-align: center;
}
.scroll-container.refresh .refresh-tip {
  display: block;
  text-align: center;
}
.scroll-container .down-tip,
.scroll-container .refresh-tip,
.scroll-container .up-tip {
  display: none;
}
.scroll-container .load-more {
  height: 3rem;
  display: flex;
  justify-content: center;
  align-items: center;
}
.scroll-container .loading {
  width: 40px;
  height: 40px;
  object-fit: contain;
  padding-bottom: 10px;
  box-sizing: border-box;
}
</style>